﻿using System;
using System.Collections.Generic;
using Co9320.NamaAlert.Server.Parameters;
using Co9320.NamaAlert.Shared;

namespace Co9320.NamaAlert.Server.BackEnd
{

	internal class ProcessQueue
	{
		/// <summary>
		/// Add,Delete and modify operation gate sentinel class
		/// </summary>
		/// <remarks>
		/// This class isn't thread safe.
		/// </remarks>
		private class GateSentinel
		{

			System.Collections.Generic.HashSet<LiveInfo> Dict_;

			public GateSentinel()
			{
				Dict_ = new HashSet<LiveInfo>();
			}

			public bool TryGetLock(ProcessObject Element)
			{
				LiveInfo info = new LiveInfo(Element.Info.StationId, Element.Info.Caster);
				
				if (Dict_.Contains(info))
				{
					return false;
				}
				else
				{
					Dict_.Add(info);
					return true;
				}
			}


			public void ReleaseLock(ProcessObject Element)
			{
				LiveInfo info = new LiveInfo(Element.Info.StationId, Element.Info.Caster);
				Dict_.Remove(info);
			}
		}


		private Queue<ProcessObject> ProcessQueue_;
		private object SyncRoot;
		private GateSentinel Gate;
		private readonly int RecursiveDepth;

		public ProcessQueue(uint RecursiveDepth)
		{
			ProcessQueue_ = new Queue<ProcessObject>();
			SyncRoot = new object();
			Gate = new GateSentinel();
			this.RecursiveDepth = (int)RecursiveDepth;
		}

		public void ReleaseLock(ProcessObject Element)
		{
			if (Element == null)
			{
				return;
			}

			var tmp = Element.Info;

			if (tmp.Caster == null || tmp.Caster.Trim().Length == 0
				|| tmp.StationId == null || tmp.StationId.Trim().Length == 0)
			{
				return;
			}

			lock (SyncRoot)
			{
				Gate.ReleaseLock(Element);
			}
		}

		public void EnqueueProcess(ProcessObject Element)
		{
			if (Element == null)
			{
				return;
			}

			var tmp = Element.Info;

			if (tmp.Caster == null || tmp.Caster.Trim().Length == 0
				|| tmp.StationId == null || tmp.StationId.Trim().Length == 0)
			{
				return;
			}

			lock (SyncRoot)
			{
				ProcessQueue_.Enqueue(Element);
			}
		}



		public ProcessObject DequeueProcess()
		{
			lock (SyncRoot)
			{
				if (ProcessQueue_.Count != 0)
				{
					return GetLock(0);
				}
				else
				{
					return null;
				}
			}
		}

		private ProcessObject GetLock(int Count)
		{
			ProcessObject Element = null;
			try
			{
				Element = ProcessQueue_.Dequeue();
			}
			catch (InvalidOperationException)
			{
				return ProcessObject.NoProcessableObject;
			}

			if (Gate.TryGetLock(Element))
			{
				return Element;
			}
			else
			{
				if (Count > RecursiveDepth)
				{
					return ProcessObject.NoProcessableObject;
				}
				else
				{
					return GetLock(++Count);
				}
			}
		}


		public bool HasValue()
		{
			lock (SyncRoot)
			{
				return ProcessQueue_.Count != 0;
			}
		}
	}
}
